home *** CD-ROM | disk | FTP | other *** search
- /* > DPRINTF.C
- * dprintf -- Source Code
- * (C) August 30 1989 Arkin Asaf
- * All rights reserved
- * References:
- * C: A Reference Manual/Chapter 17, pp 328-340
- * The Waite Group's Guide to ANSI C/Chapter 7, pp 84-87 */
-
- /* Include files: */
-
- #include <ctype.h>
- #include <setjmp.h>
- #include <stdarg.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
-
- /* Macro constants:- TRUE/FALSE, Flags mask bits, and
- * argument size types.
- * Macros:- Maximum and Minimum expand to yield the maximum
- * or minimum of two expressions; ToValue gives the decimal
- * value of an ASCII digit and ToDigit returns a digit from
- * a value in any radix. N.B.: It goes without saying that
- * the macro parameters must contain no operations of con-
- * sequence, for they will be carried out more than once. */
-
- #define TRUE 1
- #define FALSE 0
-
- #define MaskJustify 0x01 /* - Left justify value within
- * field */
- #define MaskPlusSign 0x02 /* + Precede positive value
- * with plus */
- #define MaskSpace 0x04 /* sp Precede positive value
- * with space */
- #define MaskZeros 0x08 /* 0 Justify value with zeros */
- #define MaskVarient 0x10 /* # Output value in varient
- * format */
-
- #define TypeNormal 1 /* int/double */
- #define TypeShort 2 /* short (meaningless) */
- #define TypeLong 3 /* long int */
- #define TypeDouble 4 /* long double */
-
- #define Maximum(a,b) ((a)>(b)?(a):(b))
- #define Minimum(a,b) ((a)<(b)?(a):(b))
- #define ToValue(a) ((a)-'0')
- #define ToDigit(a) ((a)<10?(a)+'0':(a)-10+'A')
-
- /* OutFunc (of type dprintf_fp) points to a putchar-like
- * function, which performs all output. Called with a
- * character int as parameter, the function returns EOF
- * only if an output error occured.*/
-
- typedef int (*dprintf_fp)(int);
- static dprintf_fp OutFunc;
-
- /* Function declarations: */
-
- int dprintf(dprintf_fp, const char *, ...);
- int vdprintf(dprintf_fp, const char *, va_list);
- static void PrintDecimal(long, int, int, char, int *);
- static void PrintRadix(unsigned long, int, int, char, char,
- int *);
- static void PrintFloat(long double, int, int, char, char,
- int *);
- static void Print(char *, char *, int, int, char, int *);
- static int ToInteger(char **, unsigned long, int, int);
- static void dputc(int);
-
- /* dputc employs this longjmp buffer in the event of an
- * output error. */
-
- jmp_buf dputc_Buf;
-
- /* int dprintf(dprintf_fp, const char *, ...)
- * dprintf accepts pointers to a putchar-like function and
- * a format string. It then passes them to vdprintf, along
- * with a pointer to the variable arguments list. */
-
- int dprintf(dprintf_fp Func, const char *Format, ...)
- {
- int Return;
- va_list Args;
-
- va_start(Args,Format);
- Return=vdprintf(Func,Format,Args);
- va_end(Args);
- return Return;
- }
-
- /* int vdprintf(dprintf_fp, const char *, va_list)
- * vdprintf is an implementation of vprintf, as defined in
- * the ANSI standard, with an additional
- * pointer-to-function as its first parameter. On exit,
- * vdprintf returns the number of characters successfully
- * printed, EOF if an error occured. */
-
- int vdprintf(dprintf_fp Func, const char *Format,
- va_list Args)
- {
- char Flags, Size, *Ptr;
- int Width, Precis, OutCnt = 0;
- long Int;
- unsigned long UnsgInt;
- long double Float;
- char *FlagsList = "-+ 0#", *TypesList = "hlL";
-
- /* The pointer-to-function assigns to static variable
- * OutFunc, rather than being passed through three layers
- * of functions. The longjmp buffer is then initialized,
- * so dputc can return in case of an output error. */
- OutFunc=Func;
- if (setjmp(dputc_Buf))
- return EOF;
- /* The format string is scanned a character at a time:
- * %'s are processed, all other characters are merely
- * echoed to the output. */
- for (; *Format; ++Format)
- {
- if (*Format!='%')
- {
- dputc(*Format);
- ++OutCnt;
- continue;
- }
- /* An output format can start with a combination of
- * five flags: - + spc 0 #. Flags is set accordingly. */
- if (!*++Format)
- return EOF;
- Flags=0;
- while ((Ptr=strchr(FlagsList,*Format))!=NULL)
- {
- Flags|=1<<(Ptr-FlagsList);
- ++Format;
- }
- /* Read width (zero assumed, if absent) and precision
- * (-1 assumed, if absent): width must not start with
- * a zero; precision precedes with a period -- if no
- * precision follows the period, zero is assumed; an
- * int argument is consumed for the width or precision,
- * if an * replaces the value of either. */
- Width=0;
- if (*Format=='*')
- {
- Width=va_arg(Args,int);
- ++Format;
- }
- else
- while (isdigit(*Format))
- Width=Width*10+ToValue(*Format++);
- if (*Format=='.')
- {
- Precis=0;
- if (*++Format=='*')
- {
- Precis=va_arg(Args,int);
- ++Format;
- }
- else
- while (isdigit(*Format))
- Precis=Precis*10+ToValue(*Format++);
- }
- else
- Precis=-1;
- /* An argument is either int (default), short int ('h'
- * -- meaningless), long int ('l'), double (default
- * float), or long double ('L'). */
- if ((Ptr=strchr(TypesList,*Format))!=NULL)
- {
- Size=Ptr-TypesList+TypeShort;
- ++Format;
- }
- else
- Size=TypeNormal;
- /* Consume one output format letter.
- * Auxiliary functions process most formats, keeping
- * vdprintf short, or else it may fail to compile. */
- switch (*Format)
- {
- case 'd':
- case 'i':
- if (Size==TypeLong)
- Int=va_arg(Args,long);
- else
- Int=va_arg(Args,int);
- PrintDecimal(Int,Width,Precis,Flags,&OutCnt);
- break;
- case 'u':
- case 'o':
- case 'x': case 'X':
- if (Size==TypeLong)
- UnsgInt=va_arg(Args,unsigned long);
- else
- UnsgInt=va_arg(Args,unsigned);
- PrintRadix(UnsgInt,Width,Precis,Flags,*Format,
- &OutCnt);
- break;
- case 'c':
- {
- static char Char[2]= {0,0};
-
- Char[0]=va_arg(Args,unsigned char);
- Print(Char,NULL,Width,-1,Flags,&OutCnt);
- break;
- }
- case 's':
- Ptr=va_arg(Args,char *);
- Print(Ptr,NULL,Width,Precis,Flags,&OutCnt);
- break;
- case 'f':
- case 'e': case 'E':
- case 'g': case 'G':
- if (Size==TypeDouble)
- Float=va_arg(Args,long double);
- else
- Float=va_arg(Args,double);
- PrintFloat(Float,Width,Precis,Flags,*Format,&OutCnt);
- break;
- case 'p':
- /* The pointer-to-void argument is cast to long
- * unsigned, assuming pointer representation to
- * remain intact. */
- UnsgInt=(unsigned long) va_arg(Args,void *);
- PrintRadix(UnsgInt,Width,Precis,Flags,*Format,
- &OutCnt);
- break;
- case 'n':
- *(va_arg(Args,int *))=OutCnt;
- break;
- case '%':
- Print("%",NULL,Width,-1,Flags,&OutCnt);
- break;
- default:
- return EOF;
- }
- }
- return OutCnt;
- }
-
- /* void PrintDecimal(long, int, int, char, int *)
- * Print a decimal value (%d or %i) with a sign prefix. */
-
- static void PrintDecimal(long Int, int Width, int Precis,
- char Flags, int *OutCnt)
- {
- char *Prefix;
- char *Buffer;
-
- if(Int<0)
- {
- Int=-Int;
- Prefix="-";
- }
- else
- {
- if (Flags&MaskPlusSign)
- Prefix="+";
- else
- if (Flags&MaskSpace)
- Prefix=" ";
- else
- Prefix=NULL;
- }
- ToInteger(&Buffer,Int,10,Precis);
- Print(Buffer,Prefix,Width,-1,Flags,OutCnt);
- free(Buffer);
- }
-
- /* void PrintRadix(unsigned long, int, int, char, char,
- * int *) Print an unsigned int in decimal (%u), octal
- * (%o), hexadecimal (%x or %X), or pointer (%p) form. In
- * the varient format octals prefix with a 0, hexadecimals
- * with a 0x, and pointers with an @. (Hexadecimal letters
- * are in the same case as is the format letter.) */
-
- static void PrintRadix(unsigned long Int, int Width, int
- Precis, char Flags, char Format,
- int *OutCnt)
- {
- char *Prefix = NULL;
- char *Buffer;
- int Length;
-
- if (Format=='u')
- {
- ToInteger(&Buffer,Int,10,Precis);
- Print(Buffer,NULL,Width,-1,Flags,OutCnt);
- }
- else
- if (Format=='o')
- {
- if (Flags&MaskVarient)
- Prefix="0";
- ToInteger(&Buffer,Int,8,Precis);
- Print(Buffer,Prefix,Width,-1,Flags,OutCnt);
- }
- else
- if (Format=='p')
- {
- /* Various architectures impose different pointer rep-
- * resentations, both in memory and in writing. As is,
- * an 8-digit hexadecimal number prints (upper case
- * letters), prefixed with an @ in the varient format. */
- if (Flags&MaskVarient)
- Prefix="@";
- ToInteger(&Buffer,Int,16,8);
- Print(Buffer,Prefix,Width,-1,Flags,OutCnt);
- }
- else
- {
- if (Flags&MaskVarient)
- Prefix=(Format=='x')?"0x":"0X";
- Length=ToInteger(&Buffer,Int,16,Precis);
- if (Format=='x')
- {
- for (; Length>0; --Length)
- Buffer[Length-1]=tolower(Buffer[Length-1]);
- }
- Print(Buffer,Prefix,Width,-1,Flags,OutCnt);
- }
- free(Buffer);
- }
-
- /* void PrintFloat(long double, int, int, char, char,
- * int *) Print a floating point number in standard (%f) or
- * engineering (%e) form; the %g format requires that the
- * shortest of the two be selected. The number divides into
- * integer, fraction and exponent parts; each is cast into
- * a long int, stringized with ToInteger, and Printed. */
-
- static void PrintFloat(long double Float, int Width, int
- Precis, char Flags, char Format,
- int *OutCnt)
- {
- char *Prefix;
- char *BufferI, *BufferF = NULL, *BufferE = NULL;
- int LengthI, LengthF, LengthE = 0;
- int Short = (Format=='g' || Format=='G');
- int Exponent = 0;
- unsigned long Int;
-
- /* Determine prefix according to sign and format flags.
- * If no precision was given, six is assumed. */
- if(Float<0)
- {
- Float=-Float;
- Prefix="-";
- }
- else
- {
- if (Flags&MaskPlusSign)
- Prefix="+";
- else
- if (Flags&MaskSpace)
- Prefix=" ";
- else
- Prefix=NULL;
- }
- if (Precis<0)
- Precis=6;
- if (Format=='e' || Format=='E' || Short)
- {
- long double TempFloat = Float;
-
- /* For %e and %g formats, establish the exponent:
- * Float is divided and multiplied by ten, until it's
- * value rests between zero and one. Exponent totals
- * all divisions minus all multiplications. */
- if (Float!=0)
- {
- while (Float>10)
- {
- Float/=10;
- ++Exponent;
- }
- while (Float<1)
- {
- Float*=10;
- --Exponent;
- }
- }
- LengthE=ToInteger(&BufferE,Exponent,10,2)+2;
- /* If the %f format is shorter, %g requires that
- * the exponent be cancelled and that amount of
- * precision be lost; It states that one precision
- * digit be lost in any case. */
- if (Short)
- {
- if (Precis>0)
- --Precis;
- if (Exponent>=-3 && Exponent<=Precis)
- {
- LengthE=0;
- Precis-=Exponent;
- Float=TempFloat;
- }
- }
- }
- /* The mantissa divides into integer and fraction parts,
- * stringized by ToInteger: the last digit always rounds
- * up; a period is printed only before a fraction or in
- * the varient format; the %g format allows trailing zeros
- * in the fraction to be lost. N.B.: Too long floating
- * numbers may raise an exception on conversion to long
- * int, or otherwise fail to convert properly. */
- Int=(unsigned long) Float;
- Float-=(long double) Int;
- if (Precis<=0 && Float>=.5)
- ++Int;
- LengthI=ToInteger(&BufferI,Int,10,-1);
- if (Precis>0)
- {
- for (LengthF=0; LengthF<Precis; ++LengthF)
- Float*=10;
- Int=(unsigned long) Float;
- Float-=(long double) Int;
- if (Float>=.5)
- ++Int;
- LengthF=ToInteger(&BufferF,Int,10,Precis);
- }
- else
- LengthF=0;
- if (Short && !(Flags&MaskVarient))
- while (LengthF>0 && BufferF[LengthF-1]=='0')
- --LengthF;
-
- if (Flags&MaskVarient || LengthF>0)
- --Width;
- Width-=LengthF+LengthE;
- Print(BufferI,Prefix,Width,-1,Flags,OutCnt);
- if (Flags&MaskVarient || LengthF>0)
- {
- dputc('.');
- ++*OutCnt;
- }
- if (LengthF>0)
- Print(BufferF,NULL,LengthF,LengthF,MaskZeros,OutCnt);
- /* Print exponent part of number, with an 'e' in the same
- * case as is the format letter. Exponents must have a
- * sign and at least two digits. */
- if (LengthE>0)
- {
- if (Format=='g')
- Format='e';
- else
- if (Format=='G')
- Format='E';
- dputc(Format);
- if (Exponent<0)
- {
- Exponent=-Exponent;
- Prefix="-";
- }
- else
- Prefix="+";
- Print(BufferE,Prefix,3,-1,MaskZeros,OutCnt);
- }
- free(BufferI);
- free(BufferF);
- free(BufferE);
- }
-
- /* int ToInteger(char **, unsigned long, int, int)
- *
- * Convert an unsigned int to a NULL-terminated string of
- * digits in the given radix. If the string has less digits
- * than the precision, additional zeros are inserted at the
- * start of it. ToInteger allocates a memory block in which
- * it stores the string -- Buffer returns its address. */
-
- static int ToInteger(char **Buffer, unsigned long Int,
- int Radix, int Precis)
- {
- int Cnt, Length;
- unsigned long TempInt = Int;
-
- if (Precis<0)
- Precis=1;
- for (Cnt=0; TempInt!=0; TempInt/=Radix, ++Cnt) ;
- *Buffer=malloc(Maximum(Precis,Cnt)+1);
- if (*Buffer==NULL)
- return 0;
- for (Length=0; Length+Cnt<Precis; )
- (*Buffer)[Length++]='0';
- Cnt= Length=Maximum(Length+Cnt,Precis);
- (*Buffer)[Length]='\0';
- for (; Int>0; Int/=Radix)
- (*Buffer)[--Cnt]=ToDigit(Int%Radix);
- return Length;
- }
-
-
- /* void Print(char *, char *, int, int, char, int *)
- * Print prefix followed by value, incrementing OutCnt by
- * the total number of characters printed: by default, the
- * string is right justified within the field with spaces;
- * the 0 flag places zeros between prefix and value; and
- * the - flag left justifies by appending spaces at the
- * end. Note that if Maximum is not -1, no more than that
- * number of characters are printed. */
-
- static void Print(char *String, char *Prefix, int Width,
- int Maximum, char Flags, int *OutCnt)
- {
- int Length = strlen(String);
-
- if (Prefix)
- Length+=strlen(Prefix);
- if (Maximum>=0 && Length>Maximum)
- Length=Maximum;
- *OutCnt+=Maximum(Length,Width);
-
- if (!(Flags&MaskJustify || Flags&MaskZeros))
- for (; Length<Width; --Width)
- dputc(' ');
- while (Prefix && *Prefix!='\0')
- {
- dputc(*Prefix++);
- --Length;
- --Width;
- }
- if (!(Flags&MaskJustify))
- for (; Length<Width; --Width)
- dputc('0');
- while (*String!='\0' && Length>0)
- {
- dputc(*String++);
- --Length;
- --Width;
- }
- for (; Width>0; --Width)
- dputc(' ');
- }
-
- /* void dputc(int)
- * Perform character output through the putchar-like
- * function provided. If it returns EOF, a longjmp to
- * vdprintf will make it return EOF to its caller. */
-
- static void dputc(int Char)
- {
- if (OutFunc(Char)==EOF)
- longjmp(dputc_Buf,EOF);
- }
-